package com.bincloud.vue.admin.common.util;

import com.bincloud.vue.admin.common.exception.BaseException;
import com.bincloud.vue.admin.common.exception.CodeMsg;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.util.List;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @Description: 验证码工具类
 * @Author: bincloud, mvpzhou
 * @Date: Create By bincloud, mvpzhou on 2022/2/24
 */
@SuppressWarnings("unused")
public class CaptchaUtils {
    private CaptchaUtils() {}

    public static class Captcha {
        /**
         * 验证码 4位
         */
        private String code;
        /**
         * 验证码图像 BASE64 位编码
         */
        private String imgStr;

        public String getCode() {
            return code;
        }

        public void setCode(String code) {
            this.code = code;
        }

        public String getImgStr() {
            return imgStr;
        }

        public void setImgStr(String imgStr) {
            this.imgStr = imgStr;
        }
    }

    private static final String DATA_IMG = "data:image/png;base64,";

    private static final Random RANDOM = new Random();

    private static final List<String> LETTER = new ArrayList<>();

    private static final int DRAW_WIDTH = 120;
    private static final int DRAW_HEIGHT = 40;
    private static final int CODE_COUNT = 4;
    private static final int CODE_OFFSET = 24;
    private static final int COLOR_START = 120;
    private static final int COLOR_END = 240;

    static {
        for (int i = '0'; i <= '9'; i++) {
            LETTER.add(String.valueOf((char) i));
        }
        for (int i = 'a'; i <= 'z'; i++) {
            LETTER.add(String.valueOf((char) i));
        }
        for (int i = 'A'; i <= 'Z'; i++) {
            LETTER.add(String.valueOf((char) i));
        }
    }

    /**
     * 验证 c1 c2 是否相等
     * @param c1 原字符
     * @param c2 对比字符
     * @param ignore 是否忽略大小写
     * @return 是否相等
     */
    public static boolean validateCaptcha(String c1, String c2, boolean ignore) {
        if (c1 == null || c2 == null) {
            return false;
        }
        if (ignore) {
            return c1.equalsIgnoreCase(c2);
        }
        return c1.equals(c2);
    }

    /**
     * 生成随机验证码值
     *
     * @param type 类型
     *             1. 数字
     *             2. 小写字母
     *             3. 大写字母
     *             4. 大小写字母
     *             5. 大小写字,数字
     * @return 返回随机值和
     */
    public static Captcha captcha(int type) {
        Captcha resDTO = new Captcha();
        String letter;
        String captcha;
        switch (type) {
            case 1:
                letter = letter(0, 10);
                break;
            case 2:
                letter = letter(10, 36);
                break;
            case 3:
                letter = letter(36, 62);
                break;
            case 4:
                letter = letter(10, 62);
                break;
            case 5:
                letter = letter(0, 62);
                break;
            default:
                throw new BaseException("不支持的类型");
        }
        captcha = generate(letter);
        resDTO.setCode(letter);
        resDTO.setImgStr(captcha);
        return resDTO;
    }

    /**
     * 获取验证码图片 Base64 String
     * @param letter 验证码
     * @return 获取验证码图片 Base64 String
     */
    public static String generate(String letter) {
        try(ByteArrayOutputStream bis = new ByteArrayOutputStream()) {
            // 图像buffer
            BufferedImage bi = new BufferedImage(DRAW_WIDTH, DRAW_HEIGHT, BufferedImage.TYPE_INT_ARGB);
            // 图形 2d
            Graphics2D g2 = bi.createGraphics();
            // 底色
            g2.setColor(Color.WHITE);
            g2.fillRect(0, 0, DRAW_WIDTH, DRAW_HEIGHT);
            // 字体
            g2.setFont(new Font("Default", Font.PLAIN, 24));
            // 写验证码
            drawString(g2, letter);
            // 释放图形上下文
            g2.dispose();
            // BASE64编码
            ImageIO.write(bi, "png", bis);
            return DATA_IMG + Base64.getEncoder().encodeToString(bis.toByteArray());
        } catch (Exception e) {
            throw new BaseException(CodeMsg.CAPTCHA_GENERATE_FAIL);
        }
    }

    /**
     * 写验证码字符
     * @param g2 图像
     * @param code 验证码
     */
    private static void drawString(Graphics2D g2, String code) {
        List<String> list = Arrays.asList(code.split(""));
        int size = list.size();
        for (int i = 0; i < size; i++) {
            g2.setColor(randomColor());
            g2.drawString(list.get(i), i * 20 + CODE_OFFSET, 30 + randomInt());
        }
    }

    /**
     * 获取 随机数 ±10 内
     * @return 随机数 ±10 内 整数
     */
    private static int randomInt() {
        int i = RANDOM.nextInt(15);
        int i1 = i & 2;
        if (i1 > 0) {
            return - RANDOM.nextInt(10);
        }
        return RANDOM.nextInt(10);
    }

    /**
     * 随机 rgb 色彩
     * @return 色彩
     */
    private static Color randomColor() {
        int r = COLOR_START + RANDOM.nextInt(COLOR_END - COLOR_START);
        int g = COLOR_START + RANDOM.nextInt(COLOR_END - COLOR_START);
        int b = COLOR_START + RANDOM.nextInt(COLOR_END - COLOR_START);
        return new Color(r, g, b);
    }

    /**
     * 获取随机数 0-9a-zA-Z之间获取
     * @return 获取随机小写字母符串
     */
    public static String letter(int start, int end) {
        List<Integer> numbers = RANDOM.ints(start, end).limit(CODE_COUNT).boxed().collect(Collectors.toList());
        StringBuilder sb = new StringBuilder(CODE_COUNT);
        for (int i = 0; i < CODE_COUNT; i++) {
            sb.append(LETTER.get(numbers.get(i)));
        }
        return sb.toString();
    }
}
